<template>
    <jb-page :show-header="false">
        <div class="chat-container">
            <div class="chat-layout">
                <div class="chat-sidebar">
                    <div class="chat-search" style="display: flex;align-items: center">
                        <n-input v-model:value="keywords" placeholder="快速搜索..."/>
                        <jb-btn style="margin-left: 8px" type="primary" icon="" @click="handleAddChat">新建聊天</jb-btn>
                    </div>

                    <div class="chat-list" ref="chatListRef" @scroll="handleSessionScroll">
                        <div v-for="(chat,index) in chats" :key="chat.id" class="chat-item"
                             :class="{ active: activeChatId === chat.id }" @click="activeChatId = chat.id">
                            <div class="chat-item-content">
                                <div class="chat-item-title">{{ chat.name || '新的会话' }}</div>
                                <div class="chat-item-time">{{ chat.createTime || '-' }}</div>
                            </div>
                            <div class="chat-actions">
                                <jb-btn :icon="Icons.EDIT" text type="warning" circle :modalComponent="ChatEdit"
                                        modalTitle="编辑会话名称" :modalComponentProps="{id:chat.id}" modalWidth="600px"
                                        @modalYesClick="handleChatNameEdit"></jb-btn>
                                <jb-btn :icon="Icons.DELETE" text type="error" circle :confirmText="'确认删除该会话？'"
                                        :url="`/aiAppSession/delete/${chat.id}`"
                                        @success="handleDeleteChat(index)"></jb-btn>
                            </div>
                        </div>
                        <div v-if="chatPageState.loading" class="loading-more">
                            <n-spin size="small"/>
                            <span>加载中...</span>
                        </div>
                    </div>
                </div>
                <div v-if="!activeChatId"
                     style="display: flex; justify-content: center;align-items: center;flex:1;font-size: 36px;font-weight: 500;color:#d5d5d5">
                    请先创建或选择一个会话
                </div>
                <div class="chat-main" v-show="activeChatId">

                    <div class="system-role">
						<textarea v-model="currentChat.config.llmConfig.systemRole"
                                  placeholder="输入系统角色设定..."></textarea>
                    </div>

                    <div class="chat_panel" style="position: relative" ref="chatContainer">

                    </div>

                </div>
            </div>


        </div>

        <!-- AI设置弹窗 - 优化后的版本 -->
        <div v-if="showAISetting" class="ai-setting-overlay" @click="handleOverlayClick">
            <div class="ai-setting-modal ai-setting-positioned" @click.stop>
                <div class="ai-setting-header">
                    <span class="ai-setting-title">AI设置</span>
                    <div class="ai-setting-close" @click="showAISetting=false">
                        <jb-icon :local="true" icon="iconamoon:close-bold" class="close-icon"></jb-icon>
                    </div>
                </div>

                <div class="ai-setting-content">
                    <n-form ref="formRef" :model="currentChat.config.llmConfig" label-placement="top"
                            label-width="120" require-mark-placement="right-hanging">
                        <n-form-item label="选择AI模型" path="name">
                            <jb-select url="aiResource/listAllEnabledType" labelField="name" placeholder="请选择AI模型"
                                       valueField="sn" v-model:value="currentChat.config.llmConfig.model"></jb-select>
                        </n-form-item>
                        <n-form-item label="MaxToken值" path="sn">
                            <div class="flex-col" style="width: 100%">
                                <n-input-number round style="width: 100%" v-model:value="currentChat.config.llmConfig.maxTokens"
                                                placeholder="请输入MaxToken值" max="8192" min="1">
                                    <template #suffix>
                                        tokens
                                    </template>
                                </n-input-number>
                                <span class="text-gray-400 font-12 pad-5 bg-gray-50 rounded-lg mt-1">范围: 1-8192</span>
                            </div>
                        </n-form-item>
                        <n-form-item label="Temperature" path="">
                            <n-input-number round style="width: 100%" v-model:value="currentChat.config.llmConfig.temperature"
                                            :precision="2" placeholder="请输入Temperature值">
                            </n-input-number>
                        </n-form-item>
                        <n-form-item label="Top P" path="">
                            <div style="display: flex;flex-direction: column">
                                <n-slider v-model:value="currentChat.config.llmConfig.topP" :step="0.1" :max="1"/>
                                <span class="text-gray-400 font-12 pad-5 bg-gray-50 rounded-lg mt-1">调节句子生成的多样性，值越大生成的内容越丰富多样，但它和Temperature只能设置一个</span>
                            </div>
                        </n-form-item>
                        <n-form-item label="Top K" path="">
                            <div style="display: flex;flex-direction: column">
                                <n-input-number round style="width: 100%" :precision="0"
                                                v-model:value="currentChat.config.llmConfig.topK" placeholder="请输入TopK值"
                                                max="100">
                                </n-input-number>
                                <span class="text-gray-400 font-12 pad-5 bg-gray-50 rounded-lg mt-1">限制每次生成时考虑的词汇数量，值越大创造性越强。该选项不是所有大模型都支持。</span>
                            </div>
                        </n-form-item>

                        <div class="context-switch">
                            <span class="context-label">开启上下文</span>
                            <n-switch size="large" v-model:value="currentChat.config.llmConfig.useContext"></n-switch>
                        </div>
                    </n-form>
                </div>
            </div>
        </div>

    </jb-page>
</template>

<script setup>
import {
    onActivated,
    onBeforeUnmount, onDeactivated,
    onMounted,
    reactive,
    ref,
    watch
} from 'vue';
import {
    Icons
} from '@/constants'
import {
    JBoltApi
} from "@/service/request/index.js";
import {
    debounce, result
} from 'lodash-es';
import ChatEdit from './components/chat-edit.vue'
import markdownit from 'markdown-it'
import AiChat from '@/components/_builtin/jb-ai-chat/index.js'
import {
    storeToRefs
} from "pinia";
import {
    useWebSocketStore
} from "@/stores/websocket.js";
import {useMessage} from 'naive-ui'
import texmath from "markdown-it-texmath";
import linkAttributes from "markdown-it-link-attributes"
import katex from "katex";


const md = markdownit({
    html: true,
    breaks: true,
    linkify: true,
    typographer: true
});
md.use(texmath, {
    engine: katex,
    delimiters: [
        "dollars", "brackets"
    ]
})
// 使用 linkAttributes 插件，并配置目标属性和 rel 属性
md.use(linkAttributes, {
    pattern: /https?:\/\/(www\.)?/i, // 匹配所有 HTTP/HTTPS 链接
    attrs: {
        target: '_blank', // 设置所有链接在新标签页打开
        rel: 'noopener' // 增加这个属性可以提高安全性
    }
})

const defaultAIConfig = {
    systemRole: '',
    maxTokens: 2000,
    topP: 0,
    topK: 0,
    model: '',
    useContext: true,
    temperature: 0.5
}
const message = useMessage()


//--------------- 左侧session列表
const appId = ref(1)
const keywords = ref('');
const chatListRef = ref(null);
const activeChatId = ref(null);
//当前会话聊天配置

const currentChat = ref({
    id: '',
    name: '',
    config: {
        llmConfig: {
            systemRole: ''
        }
    }
})
const chats = ref([]);
const chatPageState = reactive({
    pageSize: 20,
    startId: null,
    hasMore: true,
    loading: false,
});
// 监听搜索关键词变化
watch(keywords, debounce((val) => {
    resetChatList()
    loadChatList(true);
}, 300));


function resetChatList() {
    chatPageState.startId = null;
    chatPageState.hasMore = true;
    chatPageState.loading = false;
    chats.value = [];
}

async function loadChatList(isFirst) {

    if (chatPageState.loading || chatPageState.hasMore == false) return;

    chatPageState.loading = true;
    try {
        const {
            result
        } = await JBoltApi.get('/aiAppSession/getListByPage', {
            pageSize: chatPageState.pageSize,
            startId: chatPageState.startId,
            keywords: keywords.value,
            appId: appId.value, //内置应用
        });
        let {
            list,
            totalPage
        } = result.data;

        list = list ?? [];
        chatPageState.hasMore = list.length == chatPageState.pageSize;

        chatPageState.startId = list[list.length - 1]?.id;

        chats.value.push(...list)

        if (isFirst) {

            activeChatId.value = chats.value[0]?.id
        }
    } catch (error) {
        console.error('加载聊天列表失败:', error);
    } finally {
        chatPageState.loading = false;
    }
}

function handleDeleteChat(index) {
    if (activeChatId.value == chats.value[index].id) {
        activeChatId.value = null;
    }
    chats.value.splice(index, 1);


}

function handleChatNameEdit({
                                componentRef,
                                close
                            }) {
    componentRef.submit((formData) => {
        close()
        let index = chats.value.findIndex(item => item.id == formData.id)
        if (index >= 0) {
            chats.value[index].name = formData.name
        }
    })
}

function handleAddChat() {
    JBoltApi.tryPost('/aiAppSession/add', {
        name: '新的会话',
        appId: appId.value, //内置appId
        config: {
            "llmConfig": defaultAIConfig
        }
    }).then(() => {
        resetChatList();
        loadChatList(true);
    });
}

//更新session
function handleUpdateSessionName(sessionId, msg) {
    var name = msg.substring(0, 20);
    JBoltApi.tryPost('/aiAppSession/update', {
        name: name,
        id: sessionId,
        appId: appId.value
    }).then(() => {
        const itemToUpdate = chats.value.find(item => item.id === sessionId);
        if (itemToUpdate) {
            itemToUpdate.name = name;
        }
        currentChat.value.hasMsg=true;
    });
}

// 处理滚动事件
function handleSessionScroll(e) {
    const {
        scrollHeight,
        scrollTop,
        clientHeight
    } = e.target;
    // 距离底部100px时加载更多
    if (scrollHeight - scrollTop - clientHeight < 100 && !chatPageState.loading && chatPageState.hasMore) {
        loadChatList();
    }
}


//-------------右侧聊天区域
const chatContainer = ref(null);
const chatInstance = ref(null);
const chatState = ref({
    pageSize: 20,
    hasMore: true,
})
const showAISetting = ref(false)

// 处理点击遮罩层关闭弹窗
function handleOverlayClick(e) {
    // 只有点击遮罩层本身时才关闭，点击弹窗内容不关闭
    if (e.target === e.currentTarget) {
        showAISetting.value = false;
    }
}

const webSocketStore = storeToRefs(useWebSocketStore())
watch(() => webSocketStore.ws.value, () => {
    console.debug("websock重连")
    if (chatInstance.value) {
        chatInstance.value.updateWebSocket(webSocketStore.ws.value)
    }
})

watch(() => chatContainer.value, (newV) => {
    console.debug("chatContainer watch...")
    //dom初始化好了
    if (newV && activeChatId.value && !chatInstance.value) {
        loadChatInstance()
    }
})


watch(() => activeChatId.value, (newV) => {

    if (newV) {
        if (chatInstance.value) {
            chatInstance.value.destroy();
        }
        JBoltApi.tryGet(`/aiAppSession/getById/${activeChatId.value}`).then(result => {
            if (result) {
                currentChat.value = {
                    ...result.data,
                    hasMsg: false
                }

                watchConfigChange(currentChat.value)
                if (chatContainer.value) {
                    chatState.value = {
                        pageSize: 20,
                        hasMore: true
                    }
                    loadChatInstance()
                }
            }

        }).catch((error) => {
            console.error(error);
        });


    }

})


function loadChatInstance() {
    //初始化aichat
    chatInstance.value = new AiChat(chatContainer.value, {
        // websocketUrl: 'ws://192.168.31.118:8013/ws',
        websocketInstance: webSocketStore.ws.value,
        app: appId.value,
        sessionId: activeChatId.value,
        global: null,
        showAvatar: false,
        onClearMsg: async (sessionId) => {
            let res = await JBoltApi.tryGet(`/aiAppMessage/deleteBySessionId/${activeChatId.value}`);
            return true;
        },
        onMsgReceive: (data) => {

        },
        onMsgSend: (msg) => {

            if (currentChat.value.hasMsg == false) {
                //调用更新sessinoName的方法
                handleUpdateSessionName(currentChat.value.id, msg.data.content.text);

            }
        },
        onMsgToDelete: async (id) => {
            console.debug("删除消息", id)
            let {
                result, error
            } = await JBoltApi.get(`/aiAppMessage/delMsgById/${id}`)

            if (result.code == 0) {
                message.success("删除成功");
                return true;
            }
            if (error) {
                alert(error)
                return false;
            }
        },
        onSettingClick: (chatInsence) => {
            showAISetting.value = !showAISetting.value
        },
        onMarkdownRender: (markdown) => {

            return md.render(markdown);
        },
        onHistoryLoad: async (startId, global) => {

            if (chatState.value.hasMore == false) return false;

            let { result } = await JBoltApi.get('/aiAppMessage/getListByPage', {
                sessionId: activeChatId.value,
                startId,
                pageSize: chatState.value.pageSize,
            })

            let { list, totalPage } = result.data
            chatState.value.hasMore = chatState.value.pageSize == list?.length;
            console.debug("加载历史记录开始", list)

            if (!list || !list.length) {


                return {
                    msgs: [],
                    status: 'over'  // 表示没有更多数据了
                };
            }

            console.debug("加载历史记录完成...", list)
            currentChat.value.hasMsg = true;

            const transformedList = list.map(item => {
                let content;
                let renderType = "";

                if (item.role === 'assistant') {
                    // assistant的content需要解析JSON并提取内容和renderType
                    try {
                        const parsedContent = JSON.parse(item.content);
                        const firstItem = parsedContent[0];
                        content = firstItem?.content || item.content;
                        renderType = firstItem?.renderType || "";
                    } catch (e) {
                        content = item.content;
                        renderType = "";
                    }
                } else {
                    // user的content直接使用
                    content = item.content;
                    renderType = "";
                }

                return {
                    id: item.id,
                    flag: item.role == "user" ? item.id : item.linkId,
                    data: {
                        role: item.role,
                        renderType: renderType,
                        content: {
                            text: content,
                        },
                        thinking: item.thinking,
                        reference: item.reference,
                        errorMsg: item.errorMsg,
                    },
                    createTime: item.createTime
                }
            });

            // 关键：必须返回包含 msgs 属性的对象
            return {
                msgs: transformedList,
                status: chatState.value.hasMore ? 'continue' : 'over'
            };
        }
    })

}

let configMonitor = null;

function watchConfigChange(chat) {
    if (configMonitor != null) {
        configMonitor();
    }
    configMonitor = watch(() => chat.config.llmConfig, (newVal, oldVal) => {

        JBoltApi.tryPost('/aiAppConfig/updateConfigByKey', {
            linkType: "session",
            linkId: chat.id,
            configKey: "llmConfig",
            configValue: JSON.stringify(newVal)
        });
    }, {
        deep: true
    })
}


onMounted(() => {
    loadChatList(true);
});
onBeforeUnmount(() => {
    if (chatInstance.value) {
        chatInstance.value.destroy()
    }
})
onActivated(() => {
    if (!chatInstance.value && activeChatId.value) {
        chatState.value.hasMore = true
        loadChatInstance()
    }

})
onDeactivated(() => {
    if (chatInstance.value) {
        chatInstance.value.destroy()
        chatInstance.value = null;
    }
})
</script>

<style scoped lang="scss">
.chat-container {
    height: 100%;
    overflow: hidden;
}

.chat-layout {
    display: flex;
    height: 100%;
    background-color: #fff;
}

.chat-sidebar {
    width: 300px;
    border-right: 1px solid #eee;
    display: flex;
    flex-direction: column;
    height: 100%;
}

.chat-search {
    padding: 12px;
    border-bottom: 1px solid #eee;
    flex-shrink: 0;
}

.chat-list {
    flex: 1;
    overflow-y: auto;
    padding: 12px;
}

.chat-main {
    flex: 1;
    display: flex;
    flex-direction: column;
    height: 100%;
    overflow: hidden;
}

.chat-messages {
    flex: 1;
    padding: 20px;
    overflow-y: auto;
}

.chat-input {
    border-top: 1px solid #eee;
    padding: 20px;
    background: #fff;
}

.chat-item {
    padding: 12px;
    border-radius: 6px;
    cursor: pointer;
    transition: background-color 0.3s;
    margin-bottom: 8px;
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.chat-item:hover {
    background-color: rgba(0, 0, 0, 0.05);
}

.chat-item.active {
    background-color: rgba(0, 0, 0, 0.05);

    .chat-actions {
        opacity: 1;
    }
}

.chat-item-content {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 0;
}

.chat-item-title {
    font-size: 14px;
    color: #333;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.chat-item-time {
    font-size: 12px;
    color: #999;
}

.chat-actions {
    display: flex;
    gap: 4px;
    opacity: 0;
    transition: opacity 0.3s;
}

.chat-item:hover .chat-actions {
    opacity: 1;
}

.chat-actions :deep(.n-button) {
    padding: 4px;
}

.system-role {
    padding: 1rem;
}

.system-role textarea {
    width: 100%;
    padding: 0.5rem;
    border: 1px solid rgb(215 216 218);
    border-radius: 0.5rem;
    font-size: 0.875rem;
    line-height: 1.25rem;
    resize: none;
    height: 60px;
    transition: height 0.3s ease;
    outline: none;
}

.system-role textarea:focus {
    border-color: #333;
}

.system-role textarea:hover {
    height: 120px;
}

.loading-more {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 12px;
    gap: 8px;
    color: #999;
    font-size: 12px;
}

.chat_panel {
    flex: 1;
    overflow: hidden;
    padding: 1em;
}

/* AI设置弹窗样式优化 */
.ai-setting-overlay {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    //background-color: rgba(0, 0, 0, 0.5);
    z-index: 1000;
    padding: 20px;
}

.ai-setting-modal {
    background-color: #fff;
    border-radius: 16px;
    box-shadow: 0px 0px 6px 1px rgba(0, 0, 0, 0.1);
    width: 360px;
    max-height: 80vh;
    display: flex;
    flex-direction: column;
}

.ai-setting-positioned {
    position: absolute;
    bottom: 140px;
    right: 80px;
}

.ai-setting-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 18px 16px;
    border-bottom: 1px solid #eee;
    flex-shrink: 0;
}

.ai-setting-title {
    font-size: 18px;
    font-weight: 500;
    color: #333;
}

.ai-setting-close {
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    border-radius: 8px;
    transition: background-color 0.2s;
}

.ai-setting-close:hover {
    background-color: rgba(0, 0, 0, 0.05);
}

.close-icon {
    font-size: 20px;
    color: #333;
}

.ai-setting-content {
    flex: 1;
    overflow-y: auto;
    padding: 24px 16px 18px;
}

.context-switch {
    display: flex;
    justify-content: space-between;
    align-items: center;
    width: 100%;
    margin-top: 16px;
}

.context-label {
    font-size: 16px;
    color: #333;
}

/* 响应式优化 */
@media (max-width: 768px) {
    .ai-setting-positioned {
        position: fixed;
        top: 50%;
        left: 50%;
        right: auto;
        bottom: auto;
        transform: translate(-50%, -50%);
        width: calc(100vw - 40px);
        max-width: 360px;
    }
}

@media (max-height: 600px) {
    .ai-setting-positioned {
        max-height: 85vh;
        bottom: 20px;
    }

    .ai-setting-content {
        padding: 16px;
    }
}

/* 表单项间距优化 */
.ai-setting-content :deep(.n-form-item) {
    margin-bottom: 10px;
}

.ai-setting-content :deep(.n-form-item:last-child) {
    margin-bottom: 0;
}

/* 滚动条样式优化 */
.ai-setting-content::-webkit-scrollbar {
    width: 6px;
}

.ai-setting-content::-webkit-scrollbar-track {
    background: #f1f1f1;
    border-radius: 3px;
}

.ai-setting-content::-webkit-scrollbar-thumb {
    background: #c1c1c1;
    border-radius: 3px;
}

.ai-setting-content::-webkit-scrollbar-thumb:hover {
    background: #a8a8a8;
}
</style>